#include <unistd.h>
#include <stdio.h>
#include <fcntl.h>
#include <linux/fb.h>
#include <sys/mman.h>
#include <sys/ioctl.h>
#include <stdlib.h>
#include "fb.h"

static struct fb_var_screeninfo vinfo;
static struct fb_fix_screeninfo finfo;
static int fp;
static u16 *fbp;

void fb_init() {
    long screensize = 0;

    fp = open("/dev/fb0", O_RDWR);
    if (fp < 0) {
        printf("Error : Can not open framebuffer device\n");
        exit(1);
    }

    if (ioctl(fp, FBIOGET_FSCREENINFO, &finfo)) {
        printf("Error reading fixed information\n");
        exit(2);
    }

    if (ioctl(fp, FBIOGET_VSCREENINFO, &vinfo)) {
        printf("Error reading variable information\n");
        exit(3);
    }

    screensize = vinfo.xres * vinfo.yres * vinfo.bits_per_pixel / 8;
    fbp = (u16 *) mmap(NULL, screensize, PROT_READ | PROT_WRITE, MAP_SHARED, fp, 0);
    if ((char *) fbp == MAP_FAILED) {
        printf("Error: failed to map framebuffer device to memory.\n");
        exit(4);
    }
}

void fb_close() {
    munmap(fbp, vinfo.xres * vinfo.yres * vinfo.bits_per_pixel / 8);
    close(fp);
}

void compare_sorted(int *min, int *max) {
    if (*min > *max) {
        int t = *min;
        *min = *max;
        *max = t;
    }
}

void SetPixelIndex(int x, int y, u16 color) {
    *(fbp + vinfo.xres * y + x) = color;
}

u16 GetPixelIndex(int x, int y){
    return *(fbp + vinfo.xres * y + x);
}

void DrawLine(int xs, int ys, int xe, int ye, u16 color) {

}

void FillRect(int xs, int ys, int xl, int yl, u16 color) {
    int xmin, ymin, xmax, ymax;
    xmin = 0;
    ymin = 0;
    xmax = vinfo.xres;
    ymax = vinfo.yres;

    xl = xs + xl;
    yl = ys + yl;
    compare_sorted(&xs, &xl);
    compare_sorted(&ys, &yl);

    xmin = xs > xmin ? xs : xmin;
    ymin = ys > ymin ? ys : ymin;
    xmax = xl < xmax ? xl : xmax;
    ymax = yl < ymax ? yl : ymax;

    for (int j = ymin; j < ymax; j++) {
        for (int i = xmin; i < xmax; i++) {
            *(fbp + j * vinfo.xres + i) = color;
        }
    }
}
